/**
  ******************************************************************************
  * @file    zqlink.c 
  * @author  Aizbot
  * @brief   xxx.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 Aizbot.
  * All rights reserved.</center></h2>
  *
  * http://www.aizbot.com
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "zqlink.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
  * @brief  Xor Check.
  * @param  None
  * @retval None
  */
uint8_t Zqlink_Check(Zqlink_Msg_t *msg)
{
	uint8_t checksum = 0;
	uint16_t i, len;
	uint8_t len_high, len_low;

	len = msg->length;
	len_high = (uint8_t)(len >> 8);
	len_low  = (uint8_t)(len & 0xFF);
	
	for(i=0; i<msg->length; i++)
	{
		if(i ==0)
			checksum = (msg->data[i]);
		else
			checksum^= (msg->data[i]);
	}

	switch(msg->ver)
	{
	case ZQLINK_VERSION_A1:
		msg->checksum =  (checksum)^(uint8_t)(msg->length)^(msg->seq)^(msg->msgid.cmd);
		break;

	case ZQLINK_VERSION_A2:
		msg->checksum =  (checksum)^(uint8_t)(msg->length)^(msg->seq)^(msg->sysid)^(msg->compid)^(msg->msgid.cmd);
		break;

	case ZQLINK_VERSION_A3:
		msg->checksum =  (checksum)^(len_high)^(len_low)^(msg->seq)^(msg->msgid.cmd);
		break;

	case ZQLINK_VERSION_A4:
		msg->checksum =  (checksum)^(len_high)^(len_low)^(msg->seq)^(msg->sysid)^(msg->compid)^(msg->msgid.cmd);
		break;
		
	default:
		break;
	}

	return msg->checksum;
}

/**
  * @brief  xxx.
  * @param  None
  * @retval None
  */
void Zqlink_Packing(Zqlink_Msg_t *msg, Zqfifo_t *fifo)
{
	Zqfifo_Write(fifo, ZQLINK_STX1);
	Zqfifo_Write(fifo, msg->ver);
	
	if(msg->ver == ZQLINK_VERSION_A1)
	{
		Zqfifo_Write(fifo, (uint8_t)(msg->length&0x00FF));
		Zqfifo_Write(fifo, msg->seq);
	}
	else if(msg->ver == ZQLINK_VERSION_A2)
	{
		Zqfifo_Write(fifo, (uint8_t)(msg->length&0x00FF));
		Zqfifo_Write(fifo, msg->seq);
		Zqfifo_Write(fifo, msg->sysid);
		Zqfifo_Write(fifo, msg->compid);
	}
	else if(msg->ver == ZQLINK_VERSION_A3)
	{
		Zqfifo_Write(fifo, (uint8_t)(msg->length >> 8));
		Zqfifo_Write(fifo, (uint8_t)(msg->length&0x00FF));
		Zqfifo_Write(fifo, msg->seq);
	}
	else if(msg->ver == ZQLINK_VERSION_A4)
	{
		Zqfifo_Write(fifo, (uint8_t)(msg->length >> 8));
		Zqfifo_Write(fifo, (uint8_t)(msg->length&0x00FF));
		Zqfifo_Write(fifo, msg->seq);
		Zqfifo_Write(fifo, msg->sysid);
		Zqfifo_Write(fifo, msg->compid);
	}
	
	Zqfifo_Write(fifo, msg->msgid.cmd);

	for(uint16_t idx=0; idx<msg->length; idx++)
	{
		Zqfifo_Write(fifo, msg->data[idx]);
	}
	Zqfifo_Write(fifo, Zqlink_Check(msg));
	
	if(msg->tail == ZQLINK_TAIL_USE) Zqfifo_Write(fifo, ZQLINK_TAIL);
}

/**
  * @brief  xxx.
  * @param  None
  * @retval None
  */
void Zqlink_Unpack(Zqfifo_t *fifo, Zqlink_t *handle)
{
	uint8_t data;
	static uint8_t checksum;
	
	if(Timer_Out(&handle->timeout))
	{
		handle->state = ZQLINK_STATE_STX1;
		fifo->read = 0;
		fifo->write = 0;
		Timer_Free(&handle->timeout);
	}

	while(Zqfifo_Count(fifo))
	{
		data = Zqfifo_Read(fifo);
		switch(handle->state)
		{
		case ZQLINK_STATE_STX1:
			if(data != ZQLINK_STX1) handle->state = ZQLINK_STATE_STX1;
			else 
			{
				handle->state = ZQLINK_STATE_STX2;
				Timer_Set(&handle->timeout, ZQLINK_OVERFLOW_TIME);
			}
			break;
			
		case ZQLINK_STATE_STX2:
			if(data != handle->msg.ver)
			{
				handle->state = ZQLINK_STATE_STX1;
				Timer_Free(&handle->timeout);
			}
			else handle->state = ZQLINK_STATE_LEN1;
			break;

		case ZQLINK_STATE_LEN1:
			if(handle->msg.ver == ZQLINK_VERSION_A1 || handle->msg.ver == ZQLINK_VERSION_A2)
			{
				handle->msg.length = data;
				handle->state = ZQLINK_STATE_SEQ;
			}
			else if(handle->msg.ver == ZQLINK_VERSION_A3 || handle->msg.ver == ZQLINK_VERSION_A4)
			{
				handle->msg.length = data;
				handle->state = ZQLINK_STATE_LEN2;
			}
			break;

		case ZQLINK_STATE_LEN2:
			handle->msg.length = (handle->msg.length<<8) + data;
			handle->state = ZQLINK_STATE_SEQ;
			break;

		case ZQLINK_STATE_SEQ:
			if(handle->msg.ver == ZQLINK_VERSION_A1 || handle->msg.ver == ZQLINK_VERSION_A3)
			{
				handle->msg.seq = data;
				handle->state = ZQLINK_STATE_MSGCMD;
			}
			else if(handle->msg.ver == ZQLINK_VERSION_A2 || handle->msg.ver == ZQLINK_VERSION_A4)
			{
				handle->msg.seq = data;
				handle->state = ZQLINK_STATE_SYSID;
			}
			break;
			
		case ZQLINK_STATE_SYSID:
			handle->msg.sysid = data;
			handle->state = ZQLINK_STATE_COMPID;
			break;

		case ZQLINK_STATE_COMPID:
			handle->msg.compid = data;
			handle->state = ZQLINK_STATE_MSGCMD;
			break;

		case ZQLINK_STATE_MSGCMD:
			handle->msg.msgid.cmd = data;
			handle->state = ZQLINK_STATE_PAYLOAD;
			handle->index = 0;
			if(handle->msg.length == 0) handle->state = ZQLINK_STATE_CHECKSUM;
			break;

		case ZQLINK_STATE_PAYLOAD:
			if(handle->index != handle->msg.length)
				handle->msg.data[handle->index++] = data;
			if(handle->index == handle->msg.length)
				handle->state = ZQLINK_STATE_CHECKSUM;
			break;

		case ZQLINK_STATE_CHECKSUM:
			if(handle->msg.tail == ZQLINK_TAIL_NOTUSE)
			{
				handle->state = ZQLINK_STATE_STX1;
				Timer_Free(&handle->timeout);
				checksum = data;

				if(checksum == Zqlink_Check(&handle->msg))
				{
					/* Add your api function */
					handle->Api_RecSuccess(&handle->msg, handle->dev);
				}
				else 
				{
					/* Add your api function */
					handle->Api_RecError(&handle->msg, handle->dev);
				}
			}
			else
			{
				handle->state = ZQLINK_STATE_TAIL;
				checksum = data;
			}
			break;

		case ZQLINK_STATE_TAIL:
			handle->state = ZQLINK_STATE_STX1;
			Timer_Free(&handle->timeout);

			if(data == ZQLINK_TAIL && handle->msg.tail == ZQLINK_TAIL_USE)
			{
				if(checksum == Zqlink_Check(&handle->msg))
				{
					/* Add your api function */
					handle->Api_RecSuccess(&handle->msg, handle->dev);
				}
				else 
				{
					/* Add your api function */
					handle->Api_RecError(&handle->msg, handle->dev);
				}
			}
			break;
			
		default:
			break;
		}
	}
}

/************************ (C) COPYRIGHT Aizbot ********************************/
